package servicex

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"zhonghui/console/warehouse/cfgx"

	"zhonghui/console/warehouse/helperx"
	"zhonghui/console/warehouse/logx"
	"zhonghui/console/warehouse/structx"
)

func init() {
	cfgx.Fetch(cfgx.CfgItem{
		Id:    "oss",
		Group: "common",
	})
}

type OssNormalClient struct {
	Client    *oss.Client
	Bucket    string
	Domain    string
	VpcDomain string
}

// OssClient 获取oss客户端
func OssClient(bucketChannel string) (*OssNormalClient, error) {
	client, err := oss.New(
		cfgx.Cfg().GetString("oss.end_point"),
		cfgx.Cfg().GetString("oss.access_key_id"),
		cfgx.Cfg().GetString("oss.access_key_secret"),
		oss.UseCname(false),
	)
	if err != nil {
		return nil, err
	}

	bucketName := cfgx.Cfg().GetString("oss.buckets." + bucketChannel + ".name")
	bucketDomain := cfgx.Cfg().GetString("oss.buckets." + bucketChannel + ".domain")
	bucketVpcDomain := cfgx.Cfg().GetString("oss.buckets." + bucketChannel + ".vpc_domain")

	if bucketName == "" || bucketDomain == "" || bucketVpcDomain == "" {
		return nil, errors.New("invalid bucket channel")
	}

	return &OssNormalClient{
		Client:    client,
		Bucket:    bucketName,
		Domain:    bucketDomain,
		VpcDomain: bucketVpcDomain,
	}, nil
}

// OssPutBucket 上传文件
func (o *OssNormalClient) OssPutBucket(path string, content io.Reader) error {
	if path == "" {
		return errors.New("invalid path")
	}

	bucket, err := o.Client.Bucket(o.Bucket)
	if err != nil {
		return err
	}

	if err := bucket.PutObject(path, content); err != nil {
		return err
	}

	return nil
}

// OssPutBucketExtend 上传不同头类型文件
func (o *OssNormalClient) OssPutBucketExtend(path string, content io.Reader, contentType string) error {
	if path == "" {
		return errors.New("invalid path")
	}

	bucket, err := o.Client.Bucket(o.Bucket)
	if err != nil {
		return err
	}

	if err := bucket.PutObject(path, content, oss.ContentType(contentType)); err != nil {
		return err
	}

	return nil
}

// OssCopyBucket 复制文件到另外一个位置
func (o *OssNormalClient) OssCopyBucket(src, dst string) error {
	if src == "" {
		return errors.New("invalid src")
	}

	if dst == "" {
		return errors.New("invalid dest")
	}

	bucket, err := o.Client.Bucket(o.Bucket)
	if err != nil {
		return err
	}

	_, err = bucket.CopyObject(src, dst)
	if err != nil {
		return err
	}

	return nil
}

// OssFullPath 补全oss网址 objectPath第一个字符不能是/
func (o *OssNormalClient) OssFullPath(objectPath string, useHttps bool) (*structx.OssObjectFullPath, error) {
	if objectPath == "" {
		return nil, errors.New("invalid object path")
	}

	if strings.HasPrefix(objectPath, "/") {
		return nil, errors.New("first character of objectPath cannot be /")
	}

	scheme := "http://"
	if useHttps {
		scheme = "https://"
	}

	origin := fmt.Sprintf("%s%s/%s", scheme, o.Domain, objectPath)
	data := structx.OssObjectFullPath{
		Origin: origin,
		Large:  origin + "!large.jpg",
		Thumb:  origin + "!thumb.jpg",
	}

	return &data, nil
}

// OssDownload 下载文件到本地
func (o *OssNormalClient) OssDownload(objectPath, path string) error {
	if objectPath == "" {
		return errors.New("invalid object path")
	}
	if strings.HasPrefix(objectPath, "/") {
		return errors.New("first character of objectPath cannot be /")
	}

	b, err := o.Client.Bucket(o.Bucket)
	if err != nil {
		return err
	}
	if err := b.GetObjectToFile(objectPath, path); err != nil {
		return err
	}

	return nil
}

// OssDownloadStream 下载文件流
func (o *OssNormalClient) OssDownloadStream(objectPath string) (io.ReadCloser, error) {
	if objectPath == "" {
		return nil, errors.New("invalid object path")
	}

	if strings.HasPrefix(objectPath, "/") {
		return nil, errors.New("first character of objectPath cannot be /")
	}

	b, err := o.Client.Bucket(o.Bucket)
	if err != nil {
		return nil, err
	}

	return b.GetObject(objectPath)
}

// OssGetSignOrigin 获取OSS原图保护的模式下原始图片，过期时间30分钟
func (o *OssNormalClient) OssGetSignOrigin(objectPath string) string {
	b, err := o.Client.Bucket(o.Bucket)
	if err != nil {
		return ""
	}

	signUrl, _ := b.SignURL(objectPath, oss.HTTPGet, 60*30)
	return signUrl
}

// OssAvatarFullPathOrDefault 根据相对路径获取头像对象，如为空返回默认头像，不会返回空图片
func (o *OssNormalClient) OssAvatarFullPathOrDefault(avatarPath string) *structx.CommonImageData {
	obj, err := o.OssFullPath(avatarPath, true)
	if err != nil {
		objOk, _ := o.OssFullPath("zh/avatars/default_avatar_zh.png", true)
		return &structx.CommonImageData{
			Thumb: objOk.Thumb,
			Large: objOk.Large,
		}
	}

	return &structx.CommonImageData{
		Thumb: obj.Thumb,
		Large: obj.Large,
	}
}

// OssUploadWxAvatar 处理微信的头像，并保存到OSS
// objectPrefix不能以 / 开头，也不能以 / 结尾，可以使用如： test abc/def abc/def/hij
func (o *OssNormalClient) OssUploadWxAvatar(avatarUrl, objectPrefix string) (string, error) {
	if strings.HasPrefix(objectPrefix, "/") || strings.HasSuffix(objectPrefix, "/") {
		return "", errors.New("invalid object prefix or suffix")
	}

	// 处理微信头像后缀
	splitUrl := strings.Split(avatarUrl, "/")
	splitUrl[len(splitUrl)-1] = "0"
	bigUrl := strings.Join(splitUrl, "/")

	// 获取
	resp, err := http.Get(bigUrl)
	if err != nil {
		return "", err
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			return
		}
	}()

	path := fmt.Sprintf(
		"%s/avatars/%s/%s.jpg",
		objectPrefix,
		time.Now().Format("2006-01-02"),
		helperx.RandomString(32),
	)

	if err := o.OssPutBucket(path, resp.Body); err != nil {
		logx.Zap().Errorw("上传头像失败", "err", err)
		return "", err
	}

	return path, nil
}
